/*****************************************************************************
 * $LastChangedDate: 2011-09-25 12:03:59 -0400 (Sun, 25 Sep 2011) $
 * @file
 * @author  Jim E. Brooks  http://www.palomino3d.org
 * @brief   Sound sample (SDL_mixer wave).
 * @remarks Samples don't know if sound is disabled (responsibility of Sound class).
 *          [2009/12] Implemented according to SDL_mixer 1.2.10 documentation.
 *          Invalid SDL channel is used as a "not-playing flag".
 *//*
 * LEGAL:   COPYRIGHT (C) 2009 JIM E. BROOKS
 *          THIS SOURCE CODE IS RELEASED UNDER THE TERMS
 *          OF THE GNU GENERAL PUBLIC LICENSE VERSION 2 (GPL 2).
 *****************************************************************************/

#if COMPILE_SDL_MIXER

#define SOUND_SOUND_SAMPLE_SDL_WAVE_CC 1
#include "base/module.hh"
#include "base/stream.hh"
using namespace base;
#include "sound/module.hh"
#include "sound/defs.hh"
#include "sound/sound_sample.hh"
#include "sound/sound_sample_sdl_wave.hh"

namespace sound {

////////////////////////////////////////////////////////////////////////////////
////////////////////////////  SampleSDLWave  ///////////////////////////////////
////////////////////////////////////////////////////////////////////////////////

/*****************************************************************************
 * ctor
 * @param   sampleName
 * @param   mixChunk
 *          This object will then own mixChunk.
 * Throws exception if error.
 *****************************************************************************/
SampleSDLWave::SampleSDLWave( const Sample::Name& sampleName, Mix_Chunk* mixChunk )
:   Sample(sampleName),
    mVolume(defs::DEFAULT_VOLUME),
    mMixChunk(mixChunk),
    mChannel(defs::SOUND_SDL_INVALID_CHANNEL)
{
    // NOP
}

SampleSDLWave::~SampleSDLWave()
{
#if SOUND_FREE_SDL
    // void Mix_FreeChunk(Mix_Chunk *chunk)
    // chunk : Pointer to the Mix_Chunk to free. 
    // Free the memory used in chunk, and free chunk itself as well.
    // Do not use chunk after this without loading a new sample to it.
    // Note: It's a bad idea to free a chunk that is still being played... 
    //                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    // [jimb] Mix_FreeChunk() will ensure it is stopped first.

    // Free SDL objects.
    if ( mMixChunk != NULL )
        Mix_FreeChunk( mMixChunk );

#endif // SOUND_FREE_SDL
}

/*****************************************************************************
 * @return True if sample is playing.
 *****************************************************************************/
bool
SampleSDLWave::IfPlaying( void )
{
CHECK_TYPESIG(this,TYPESIG_SOUND_SAMPLE);

    // int Mix_Playing(int channel)
    // channel : Channel to test whether it is playing or not.
    //           -1 will tell you how many channels are playing. 
    // Tells you if channel is playing, or not.
    // Note: Does not check if the channel has been paused.
    // Returns: Zero if the channel is not playing.
    // Otherwise if you passed in -1, the number of channels playing is returned.
    // If you passed in a specific channel, then 1 is returned if it is playing. 

    if ( EX( IfValidChannel() ) )
        return Mix_Playing( mChannel );
    else
        return false;  // not playing
}

/*****************************************************************************
 * Play sound sample.
 * @param   playLoop
 *          Play continuously or once.
 * @param   volume
 *          Default is DEFAULT_VOLUME.
 * @return False if error.
 *****************************************************************************/
bool
SampleSDLWave::Play( const defs::ePlayLoop playLoop, const fp volume )
{
CHECK_TYPESIG(this,TYPESIG_SOUND_SAMPLE);

    // Mix_PlayChannel():
    // int Mix_PlayChannel(int channel, Mix_Chunk *chunk, int loops);
    // channel : Channel to play on, or -1 for the first free unreserved channel.
    // chunk   : Sample to play.
    // loops   : Number of loops, -1 is infinite loops.
    //           Passing one here plays the sample twice (1 loop). 
    // Play chunk on channel, or if channel is -1, pick the first free unreserved channel.
    // The sample will play for loops+1 number of times, unless stopped by halt, or fade out,
    // or setting a new expiration time of less time than it would have originally taken to play the loops,
    // or closing the mixer.
    // Note: this just calls Mix_PlayChannelTimed() with ticks set to -1.
    // Returns: the channel the sample is played on. On any errors, -1 is returned.

    // Try to allocate a channel to play sample in.
    mChannel = Mix_PlayChannel( -1,
                                mMixChunk,
                                (playLoop == defs::PLAY_LOOP) ? -1 : 0 );  // 0 (not 1) means play once
    ASSERT( defs::SOUND_SDL_INVALID_CHANNEL == -1 );  // check symbolic name
    if ( EX( mChannel != defs::SOUND_SDL_INVALID_CHANNEL ) )
    {
        // Got a valid channel.  Now set its volume.
        if ( SetVolume( volume ) )
        {
            return true;
        }
        else
        {
            #if LOG_SOUND_ERRORS
            CERROR << "ERROR[SampleSDLWave::Play] allocated channel but setting volume failed " << GetName() << std::endl;
            PRINT_SDL_ERROR();
            #endif

            return false;
        }
    }
    else
    {
        // Failed to get a channel to play sample in.
        #if LOG_SOUND_ERRORS
        CERROR << "ERROR[SampleSDLWave::Play] failed to allocate channel " << GetName() << std::endl;
        PRINT_SDL_ERROR();
        #endif

        return false;
    }
}

/*****************************************************************************
 * Stop playing a sound sample.
 * @return False if error.
 *****************************************************************************/
bool
SampleSDLWave::Stop( void )
{
CHECK_TYPESIG(this,TYPESIG_SOUND_SAMPLE);

    // int Mix_HaltChannel(int channel)
    // channel : Channel to stop playing, or -1 for all channels.
    // Halt channel playback, or all channels if -1 is passed in.
    // Any callback set by Mix_ChannelFinished will be called.
    // Returns: always returns zero. (kinda silly)

    if ( EX( IfValidChannel() ) )
    {
        // Forget channel so that Play() can allocate another free one.
        const bool stopped = (Mix_HaltChannel(mChannel) == 0);
        mChannel = defs::SOUND_SDL_INVALID_CHANNEL;  // invalidate after stopping
        if ( EX( stopped ) )
        {
            return true;
        }
        else
        {
            #if LOG_SOUND_ERRORS
            CERROR << "ERROR[SampleSDLWave::Stop] Mix_HaltChannel() " << GetName() << std::endl;
            PRINT_SDL_ERROR();
            #endif

            return false;
        }
    }
    else
    {
        // Isn't playing (ok).
        return true;
    }
}

/*****************************************************************************
 * Set volume of a sound sample.
 * @return False if error.
 *****************************************************************************/
bool
SampleSDLWave::SetVolume( const fp volume )
{
CHECK_TYPESIG(this,TYPESIG_SOUND_SAMPLE);

    // int Mix_Volume(int channel, int volume)
    // channel : Channel to set mix volume for.
    //           -1 will set the volume for all allocated channels. 
    // volume  : The volume to use from 0 to MIX_MAX_VOLUME(128).
    //           If greater than MIX_MAX_VOLUME,
    //           then it will be set to MIX_MAX_VOLUME.
    //           If less than 0 then the volume will not be set. 
    // Set the volume for any allocated channel. If channel is -1 then all channels
    // at are set at once. The volume is applied during the final mix,
    // along with the sample volume. So setting this volume to 64 will halve
    // the output of all samples played on the specified channel.
    // All channels default to a volume of 128, which is the max.
    // Newly allocated channels will have the max volume set,
    // so setting all channels volumes does not affect subsequent channel allocations.
    // Returns: current volume of the channel. If channel is -1, the average volume is returned. 

    if ( EX( IfValidChannel() ) )
    {
        mVolume = volume;
        Mix_Volume( mChannel, volume * MIX_MAX_VOLUME );
        return true;
    }
    else
    {
        #if LOG_SOUND_ERRORS
        CERROR << "ERROR[SampleSDLWave:SetVolume] invalid channel " << GetName() << std::endl;
        PRINT_SDL_ERROR();
        #endif

        return false;
    }
}

/*****************************************************************************
 * @return Volume of a sound sample.
 *****************************************************************************/
fp
SampleSDLWave::GetVolume( void )
{
CHECK_TYPESIG(this,TYPESIG_SOUND_SAMPLE);

    return mVolume;
}

} // namespace sound

#endif // COMPILE_SDL_MIXER
